package com.link.base.base.common.service;

import com.link.base.base.common.dao.mybatis.mapper.AttachmentMapper;
import com.link.base.base.common.dao.mybatis.mapper.InterObjectMapper;
import com.link.base.base.common.model.Announce;
import com.link.base.base.common.model.Attachment;
import com.link.base.core.basic.util.YmlConstants;
import com.link.base.user.model.CoreUser;
import com.link.core.basic.dao.KeyGenerateDao;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.model.UploadImgInfo;
import com.link.core.basic.query.Filter;
import com.link.core.basic.query.QueryParams;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.ServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.modules.export.FileIndexGenarate;
import com.link.core.util.*;
import com.link.core.util.redisclient.LinkRedisClient;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.GeneratePresignedUrlRequest;
import com.qcloud.cos.model.ResponseHeaderOverrides;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.utils.DateUtils;
import com.tencent.cloud.CosStsClient;
import org.apache.commons.io.FileUtils;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Copyright  hande Company. All Rights Reserved.
 *
 * @author unknwon
 * @version 1.0
 * @date: 2018/11/9 9:12
 */

@Service
@Transactional(rollbackFor = ServiceException.class)
public class AttachmentServiceImpl extends BasicServiceImpl<Attachment> implements AttachmentService {
    /**
     *  腾讯云COS账号 secretId
     */
    private static final String SECRET_ID = "AKIDFGy70wiUosuhpJkcPwEG4h6IfBQQfnre";
    /**
     * 腾讯云COS账号 secretKey
     */
    private static final String SECRET_KEY = "pOincGeqoX4LQGuIOIwKTp9EApEMHvJU";
    /**
     * 腾讯云COS临时凭证缓存对象key
     */
    private static final String COS_TMP_SECRET = "cos_tmp_secret";

    @Resource
    private AttachmentMapper attachmentMapper;

    @Resource
    private KeyGenerateDao keyGenerateDao;

    @Resource
    private InterObjectMapper interObjectMapper;

    private static Random RANDOM = new Random();

    static BASE64Encoder encoder = new sun.misc.BASE64Encoder();

    @Override
    public BasicMapper<Attachment> getBasicMapper() {
        return attachmentMapper;
    }

    /**
     * 单个附件上传
     *
     * @param myfile
     * @param record
     * @throws ServiceException
     */
    @Override
    public void upload(MultipartFile myfile, Attachment record) throws ServiceException {
        CoreUser user = UserUtil.getUser();
        String realativPath = "/web/file/" + user.getId().toString() + "/" + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        String originalFilename = null;

        originalFilename = myfile.getOriginalFilename();
        if (null == myfile) {
            throw new ServiceException("PUBLIC-017");
        } else {
            SimpleDateFormat datetimeFormatter = new SimpleDateFormat("yyyyMMddHHmmsss");
            String newfileName = datetimeFormatter.format(new Date()) + "_" + FileIndexGenarate.getFileIndex(new Date())
                    + originalFilename.substring(originalFilename.lastIndexOf('.'));
            record.setAttachmentName(newfileName);
            record.setAttachmentPath(realativPath + "/" + newfileName);
            record.setAttachmentOriginal(originalFilename);
            record.setAttachmentSize(String.valueOf(myfile.getSize()));
            //文件类型(后缀)
            String fileType = "";
            try {
                fileType = originalFilename.substring(originalFilename.lastIndexOf('.') + 1).toLowerCase();
            } catch (Exception e) {
                LCLogger.withException(e);
                fileType = "";
            }
            record.setAttachmentType(fileType == null ? "" : fileType);
            try {
                FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, newfileName));
            } catch (IOException e) {
                LCLogger.withException(e);
                throw new ServiceException(e.getMessage());
            }
            String jpg = "jpg";
            String png = "png";
            String bmp = "bmp";
            String jpeg = "jpeg";
            if (jpg.equals(fileType) || png.equals(fileType) || bmp.equals(fileType) || jpeg.equals(fileType)) {
                try {
                    String strImg = realPath + "/" + newfileName;
                    strImg = strImg.substring(strImg.lastIndexOf(",") + 1);
                    String binaryImg = getImageBinary(strImg, fileType);
                    UploadImgInfo ui = ImageUtil.saveImageAndLessImage(binaryImg, String.valueOf(user.getId()), String.valueOf(record.getId()));
                    record.setSmallurl(ui.getFileLessPath());
                    record.setBigurl(ui.getBigfilePath());
                } catch (Exception e) {
                    LCLogger.withException(e);
                }
            }
        }
    }

    /**
     * 图片转二进制字符串
     *
     * @param strImg 图片路径
     * @param fileType 图片类型
     * @return binaryImg 图片的二进制字符串
     * @throws Exception
     */
    public static String getImageBinary(String strImg, String fileType) {
        File file = new File(strImg);
        BufferedImage bi;
        String binaryImg = "";
        try {
            bi = ImageIO.read(file);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            //转换的图片是格式这里就什么格式，否则会失真
            ImageIO.write(bi, fileType, byteArrayOutputStream);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            binaryImg = encoder.encodeBuffer(bytes).trim();
            return binaryImg;
        } catch (IOException e) {
            LCLogger.withException(e);
        }
        return null;
    }

    /**
     * 上传文件并且新建附件记录
     *
     * @param myfile 文件
     * @param entity 附件对象
     * @return
     * @throws Exception
     */
    @Override
    public Attachment insert(MultipartFile myfile, Attachment entity) throws Exception {
        if (myfile == null) {
            throw new ServiceException("PUBLIC-013");
        }
        beforInsert(entity);
        // 插入附件表
        // 附件上传
        upload(myfile, entity);
        // 向附件表插入附件基础信息
        insert(entity);
        Attachment newEntity = queryById(entity);
        return newEntity;
    }

    @Override
    public void insert(Attachment entity) throws Exception {
        // 若新建时未传递上传类型字段uploadType，默认为原始附件上传
        if (StringUtils.isBlank(entity.getUploadType())) {
            entity.setUploadType("origin");
        }
        beforInsert(entity);
        attachmentMapper.insert(entity);
    }

    @Override
    public void beforInsert(Attachment entity) throws Exception {
        // cos和普通文件上上传均需校验
        // headId：用于判断当前附件处于哪个上传页面或功能，cos考虑通过headId获取同一功能下的所有附件
        // moduleType: 模块类型
        if (null == entity.getHeadId()) {
            throw new ServiceException("PUBLIC-018");
        }
        if (StringUtils.isBlank(entity.getModuleType())) {
            throw new ServiceException("PUBLIC-019");
        }
    }

    /**
     * 查询资源库列表
     *
     * @param record
     * @return
     * @throws Exception
     */
    @Override
    public List<Attachment> queryAttachmentLibraryListPage(Attachment record) throws Exception {
        return attachmentMapper.queryAttachmentLibraryListPage(record);
    }

    /**
     * 删除文件
     *
     * @param filePath 文件的服务器路径
     * @throws ServiceException
     */
    @Override
    public void fileDelete(String filePath) throws ServiceException {
        try {
            File file = new File(filePath);
            if (file.exists() && file.isFile()) {
                boolean delFlag = file.delete();
                if (delFlag) {
                    LCLogger.info().withMessageKey("fileDelete")
                            .withMessage("删除文件成功").flush();
                } else {
                    LCLogger.info().withMessageKey("fileDelete")
                            .withMessage("删除文件失败").flush();
                }
            }
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 查询公告下的附件列表
     *
     * @param record
     * @return
     * @throws ServiceException
     */
    @Override
    public List<Attachment> queryAttachmentListByAnnounceId(Announce record) throws ServiceException {
        List<Attachment> list = null;
        try {
            list = attachmentMapper.queryAttachmentListByAnnounceId(record);
            for (int i = 0; i < list.size(); i++) {
                //获取附件的相对路径并拼成绝对路径
                String realPath = YmlConstants.getPortalShareUrlDir() + list.get(i).getAttachmentPath()
                        + "/" + list.get(i).getAttachmentName();

                list.get(i).setAttachmentRealPath(realPath);
            }
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }
        return list;
    }

    /**
     * 附件上传，其中方法的参数为MultipartFile[]
     *
     * @param myfiles
     * @return
     * @throws ServiceException
     */
    @Override
    public List<Attachment> attachmentUpload(MultipartFile[] myfiles) throws ServiceException {
        List<Attachment> list = new ArrayList<Attachment>();
        CoreUser user = UserUtil.getUser();
        String realativPath = "/file/" + user.getId() + "/" + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        String originalFilename = null;
        for (MultipartFile myfile : myfiles) {
            Attachment result = new Attachment();
            originalFilename = myfile.getOriginalFilename();
            if (myfile.isEmpty()) {
                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "请选择文件后上传").flush();
                throw new ServiceException("PUBLIC-017");
            } else {

                String newfileName = user.getId() + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss")
                        + RANDOM.nextInt(100) + originalFilename.substring(originalFilename.lastIndexOf('.'));
                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "生成文件名称: " + newfileName).flush();
                result.setAttachmentName(newfileName);

                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "原始文件名称: " + originalFilename).flush();
                result.setAttachmentOriginal(originalFilename);

                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件大小: " + myfile.getSize()).flush();
                result.setAttachmentSize(String.valueOf(myfile.getSize()));

                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件类型: " + myfile.getContentType()).flush();
                result.setAttachmentType(myfile.getContentType());

                LCLogger.info().withMessageKey("attachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件路径: " + realativPath).flush();
                result.setAttachmentPath(realativPath);
                try {
                    FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, newfileName));
                } catch (IOException e) {
                    LCLogger.withException(e);
                    LCLogger.error().withMessageKey("attachmentUpload")
                            .withMessage(AppConstants.LOG_DIVISION_LINE + e.getMessage()).flush();
                    throw new ServiceException(e.getMessage());
                }
            }
            list.add(result);
        }
        return list;
    }

    /**
     * 附件上传，其中方法的参数为List
     *
     * @param myfiles
     * @return
     * @throws ServiceException
     */
    @Override
    public List<Attachment> attachmentListUpload(List<MultipartFile> myfiles) throws ServiceException {
        List<Attachment> list = new ArrayList<Attachment>();
        CoreUser user = UserUtil.getUser();
        String realativPath = "/file/" + user.getId() + "/" + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        String originalFilename = null;
        for (MultipartFile myfile : myfiles) {
            Attachment result = new Attachment();
            originalFilename = myfile.getOriginalFilename();
            if (myfile.isEmpty()) {
                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "请选择文件后上传").flush();
                throw new ServiceException("PUBLIC-017");
            } else {
                String newfileName = user.getId() + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss")
                        + RANDOM.nextInt(100) + originalFilename.substring(originalFilename.lastIndexOf('.'));
                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "生成文件名称: " + newfileName).flush();
                result.setAttachmentName(newfileName);
                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "原始文件名称: " + originalFilename).flush();
                result.setAttachmentOriginal(originalFilename);

                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件大小: " + myfile.getSize()).flush();
                result.setAttachmentSize(String.valueOf(myfile.getSize()));

                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件类型: " + myfile.getContentType()).flush();
                result.setAttachmentType(myfile.getContentType());

                LCLogger.info().withMessageKey("attachmentListUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件路径: " + realativPath).flush();
                result.setAttachmentPath(realativPath);
                try {
                    FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, newfileName));
                } catch (IOException e) {
                    LCLogger.withException(e);
                    LCLogger.error().withMessageKey("attachmentListUpload")
                            .withMessage(AppConstants.LOG_DIVISION_LINE + e.getMessage()).flush();
                    throw new ServiceException(e.getMessage());
                }
            }
            list.add(result);
        }
        return list;
    }

    /**
     * 上传附件并覆盖原有附件
     *
     * @param myfiles
     * @return
     * @throws ServiceException
     */
    @Override
    public List<Attachment> attachmentUploadAndCover(MultipartFile[] myfiles) throws ServiceException {
        List<Attachment> list = new ArrayList<Attachment>();
        CoreUser user = UserUtil.getUser();
        //同一个用户上传的附件都会放在同一个文件夹下
        String realativPath = "/file/onlyOne" + user.getId();
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        File oldPath = new File(realPath);
        //删除完该路径下的文件
        deleteFile(oldPath);
        String originalFilename = null;
        for (MultipartFile myfile : myfiles) {
            Attachment result = new Attachment();
            originalFilename = myfile.getOriginalFilename();

            if (myfile.isEmpty()) {
                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "请选择文件后上传").flush();
                throw new ServiceException("PUBLIC-017");
            } else {
                String newfileName = user.getId() + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss")
                        + RANDOM.nextInt(100) + originalFilename.substring(originalFilename.lastIndexOf('.'));
                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "生成文件名称: " + newfileName).flush();
                result.setAttachmentName(newfileName);
                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "原始文件名称: " + originalFilename).flush();
                result.setAttachmentOriginal(originalFilename);

                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件大小: " + myfile.getSize()).flush();
                result.setAttachmentSize(String.valueOf(myfile.getSize()));

                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件类型: " + myfile.getContentType()).flush();
                result.setAttachmentType(myfile.getContentType());

                LCLogger.info().withMessageKey("attachmentUploadAndCover")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件路径: " + realativPath).flush();
                result.setAttachmentPath(realativPath);
                try {
                    FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, newfileName));
                } catch (IOException e) {
                    LCLogger.withException(e);
                    LCLogger.error().withMessageKey("attachmentUploadAndCover")
                            .withMessage(AppConstants.LOG_DIVISION_LINE + e.getMessage()).flush();
                    throw new ServiceException(e.getMessage());
                }
            }
            list.add(result);
        }
        return list;
    }

    /**
     * 此方法系工具方法，作用：删除文件
     *
     * @param oldPath
     */
    public void deleteFile(File oldPath) {
        if (oldPath.isDirectory()) {
            System.out.println(oldPath + "是文件夹--");
            File[] files = oldPath.listFiles();
            for (File file : files) {
                deleteFile(file);
            }
        } else {
            boolean delFlag = oldPath.delete();
            if (delFlag) {
                LCLogger.info().withMessageKey("deleteFile")
                        .withMessage("删除文件成功").flush();
            } else {
                LCLogger.info().withMessageKey("deleteFile")
                        .withMessage("删除文件失败").flush();
            }
        }
    }

    /**
     * 编辑公告时，删除公告下的附件
     *
     * @param record
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void deleteAttachmentInAnnounceId(Attachment record) throws ServiceException {
        try {
            Attachment param = new Attachment();
            param.setId(record.getId());
            List<Attachment> list = attachmentMapper.queryAll(param);
            if (list.size() == 0) {
                throw new ServiceException("PUBLIC-014");
            }
            Attachment attachment = list.get(0);
            attachmentMapper.deleteById(record);
            interObjectMapper.deleteAnnounceAttachByAttachId(record);
            String fileUrl = attachment.getAttachmentRelativePath();
            File file = new File(fileUrl);
            if (file.exists() && file.isFile()) {
                boolean delFlag = file.delete();
                if (delFlag) {
                    LCLogger.info().withMessageKey("deleteAttachmentInAnnounceId")
                            .withMessage("删除文件成功").flush();
                } else {
                    LCLogger.info().withMessageKey("deleteAttachmentInAnnounceId")
                            .withMessage("删除文件失败").flush();
                }
            }
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }

    }

    /**
     * 根据附件Id查询附件隶属的公告信息
     *
     * @param record
     * @return
     * @throws ServiceException
     */
    @Override
    public Announce queryAnnouceByAttachmentId(Attachment record) throws ServiceException {
        Announce announce;
        try {
            announce = attachmentMapper.queryAnnouceByAttachmentId(record);
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }
        return announce;
    }

    /**
     * 多文件打包下载
     *
     * @param ids 文件id字符串组
     * @return
     */
    @Override
    public String batchDownload(String[] ids) {
        ZipOutputStream out = null;
        try {
            byte[] buffer = new byte[1024];
            CoreUser user = UserUtil.getUser();
            String zipFileName = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss") + "_" + RANDOM.nextInt(100) + ".zip";
            String relativePath = "/download/" + user.getId() + "/";
            //文件在服务器上的绝对路径
            String absolutePath = YmlConstants.getPortalShareFileDir() + relativePath;
            File file = new File(absolutePath);
            if (!file.exists() && !file.isDirectory()) {
                System.out.println("file: '" + absolutePath + "' is not exists!");
                file.mkdirs();
            }
            out = new ZipOutputStream(new FileOutputStream(absolutePath + zipFileName));
            File[] files = new File[ids.length];
            Attachment entity = new Attachment();
            String warning = "";
            for (int i = 0; i < ids.length; i++) {
                entity.setId(Long.parseLong(ids[i]));
                Attachment attachment = attachmentMapper.queryById(entity);
                if (attachment != null) {
                    files[i] = new File(YmlConstants.getPortalShareFileDir() + attachment.getAttachmentFilePath());
                    if (!files[i].exists() || files[i].isDirectory()) {
                        warning += attachment.getAttachmentOriginal() + "; " + System.getProperty("line.separator");
                        continue;
                    }
                    try (FileInputStream in = new FileInputStream(files[i])) {
                        out.putNextEntry(new ZipEntry(files[i].getName()));
                        int len;
                        // 读入需要下载的文件的内容，打包到zip文件
                        while ((len = in.read(buffer)) > 0) {
                            out.write(buffer, 0, len);
                        }
                        out.closeEntry();
                        in.close();
                        files[i] = null;
                    }
                }
            }
            if (!"".equals(warning)) {
                warning += System.getProperty("line.separator") + "===========以上文件不存在或已删除";
                File warningTxt = new File("warning.txt");
                if (!warningTxt.exists()) {
                    boolean newFlag = warningTxt.createNewFile();
                    if (newFlag) {
                        LCLogger.info().withMessageKey("batchDownload")
                                .withMessage("创建文件成功").flush();
                    } else {
                        LCLogger.info().withMessageKey("batchDownload")
                                .withMessage("创建文件失败").flush();
                    }
                }
                try (FileOutputStream fos = new FileOutputStream(warningTxt)) {
                    try (OutputStreamWriter osw = new OutputStreamWriter(fos)) {
                        osw.write(warning);
                        osw.flush();
                        osw.close();
                        fos.close();
                        try (FileInputStream in = new FileInputStream(warningTxt)) {
                            out.putNextEntry(new ZipEntry(warningTxt.getName()));
                            int len;
                            // 读入需要下载的文件的内容，打包到zip文件
                            while ((len = in.read(buffer)) > 0) {
                                out.write(buffer, 0, len);
                            }
                            out.closeEntry();
                            in.close();
                            boolean delFlag = warningTxt.delete();
                            if (delFlag) {
                                LCLogger.info().withMessageKey("batchDownload")
                                        .withMessage("删除文件成功").flush();
                            } else {
                                LCLogger.info().withMessageKey("batchDownload")
                                        .withMessage("删除文件失败").flush();
                            }
                            warningTxt = null;
                        }
                    }
                }
            }
            return YmlConstants.getPortalShareUrlDir() + relativePath + zipFileName;
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                LCLogger.withException(e);
            }
        }
    }

    /**
     * 邮件附件上传
     */
    @Override
    public List<Attachment> emailAttachmentUpload(List<MultipartFile> myfiles) throws ServiceException {
        List<Attachment> list = new ArrayList<Attachment>();
        CoreUser user = UserUtil.getUser();
        String realativPath = "/file/" + user.getId() + "/" + "emailTemp";
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        String originalFilename = null;
        for (MultipartFile myfile : myfiles) {
            Attachment result = new Attachment();
            originalFilename = myfile.getOriginalFilename();
            if (originalFilename.lastIndexOf('.') == -1) {
                throw new ServiceException("PUBLIC-020");
            }
            if (myfile.isEmpty()) {
                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "请选择文件后上传").flush();
                throw new ServiceException("PUBLIC-017");
            } else {
                String newfileName = myfile.getOriginalFilename();
                newfileName = user.getId() + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss")
                        + RANDOM.nextInt(100) + originalFilename.substring(originalFilename.lastIndexOf('.'));
                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "生成文件名称: " + newfileName).flush();
                result.setAttachmentName(newfileName);
                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "原始文件名称: " + originalFilename).flush();
                result.setAttachmentOriginal(originalFilename);

                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件大小: " + myfile.getSize()).flush();
                result.setAttachmentSize(String.valueOf(myfile.getSize()));

                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件类型: " + myfile.getContentType()).flush();
                result.setAttachmentType(myfile.getContentType());

                LCLogger.info().withMessageKey("emailAttachmentUpload")
                        .withMessage(AppConstants.LOG_DIVISION_LINE + "文件路径: " + realativPath).flush();
                result.setAttachmentPath(realativPath);
                try {
                    File file = new File(realPath, newfileName);
                    if (!file.exists()) {
                        FileUtils.copyInputStreamToFile(myfile.getInputStream(), file);
                    }
                } catch (IOException e) {
                    LCLogger.withException(e);
                    LCLogger.error().withMessageKey("emailAttachmentUpload")
                            .withMessage(AppConstants.LOG_DIVISION_LINE + e.getMessage()).flush();
                    throw new ServiceException(e.getMessage());
                }
            }
            list.add(result);
        }
        return list;
    }

    /**
     * 删除邮件附件
     *
     * @param fileName
     * @throws ServiceException
     */
    @Override
    public void emailAttachmentDelete(String fileName) throws ServiceException {
        if ("".equals(fileName) || fileName == null) {
            throw new ServiceException("PUBLIC-014");
        } else {
            CoreUser user = UserUtil.getUser();
            String realativPath = "/file/" + user.getId() + "/" + "emailTemp";
            String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
            File file = new File(realPath, fileName);
            if (file.exists()) {
                boolean delFlag = file.delete();
                if (delFlag) {
                    LCLogger.info().withMessageKey("emailAttachmentDelete")
                            .withMessage("删除文件成功").flush();
                } else {
                    LCLogger.info().withMessageKey("emailAttachmentDelete")
                            .withMessage("删除文件失败").flush();
                }
            }
        }
    }

    /**
     * 下载邮件附件
     *
     * @param fileName
     * @param response
     */
    @Override
    public void emailAttachmentDownload(String fileName, HttpServletResponse response) {
        int status = 0;
        byte[] b = new byte[1024];
        FileInputStream in = null;
        ServletOutputStream out2 = null;
        try {
            response.setContentType("application/x-download");
            response.setHeader("content-disposition", "attachment; filename=" + fileName);
            CoreUser user = UserUtil.getUser();
            String realativPath = "/file/" + user.getId() + "/" + "emailTemp";
            String fileUrl = YmlConstants.getPortalShareFileDir() + realativPath + "/" + fileName;
            File file = new File(fileUrl);
            if (!file.exists()) {
                throw new ServiceException("PUBLIC-014");
            }
            in = new FileInputStream(fileUrl);
            out2 = response.getOutputStream();
            while (status != -1) {
                status = in.read(b);
                out2.write(b);
            }
            out2.flush();
        } catch (Exception e) {
            LCLogger.withException(e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LCLogger.withException(e);
                }
            }
            if (out2 != null) {
                try {
                    out2.close();
                } catch (IOException e) {
                    LCLogger.withException(e);
                }
            }
        }
    }

    /***
     * 通过headID删除附件
     * @param attachment
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void deleteByHeadId(Attachment attachment) throws Exception {
        attachmentMapper.deleteByHeadId(attachment);
    }

    @Override
    public List<Attachment> queryAttachmentByHeadId(Attachment attachment) throws Exception {
        return attachmentMapper.queryAttachmentByHeadId(attachment);
    }

    @Override
    public Attachment queryVoiceAttachmentByHeadId(Long headId) throws Exception {
        return attachmentMapper.queryVoiceAttachmentByHeadId(headId);
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void deleteAttachentById(Long id) throws ServiceException {
        try {
            if (null == id) {
                throw new RuntimeException("PUBLIC-007");
            }
            Attachment attachment = new Attachment();
            attachment.setId(id);
            attachmentMapper.deleteById(attachment);
            // 删除指定路径下的文件
            fileDelete(attachment.getAttachmentRelativePath());
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 上传单个附件
     *
     * @param multiFile
     * @param record
     * @return
     * @throws ServiceException
     */
    @Override
    public Attachment voiceFileUpload(MultipartFile multiFile, Attachment record) throws ServiceException {

        String moduleType = record.getModuleType();
        CoreUser user = UserUtil.getUser();
        String loginId = user.getLogin();
        String commonName = null;
        String fileName = null;
        if (record.getAttachmentName() == null || "".equals(record.getAttachmentName())) {
            commonName = loginId + DateUtil.dateToStr(new Date(), "HHmmss_SSS");
            fileName = commonName + ".wav";
        } else {
            fileName = record.getAttachmentName();
        }

        // 文件名

        String fileRelatePath = "";
        // 存放附件的根路径
        String path = YmlConstants.getPortalShareUrlDir();
        // 模块类型
        String modelPath = "/app/" + moduleType + "/voice";
        String endPath = "/" + modelPath + "/" + fileName;
        fileRelatePath = endPath;
        // 文件存放路径+文件名
        String filePath = path + endPath;
        // 目标文件路径
        String fileFolder = path + "/" + modelPath;
        String originalFilename = multiFile.getOriginalFilename();
        try {
            File file = new File(fileFolder);
            if (!file.exists()) {
                LCLogger.info().withMessageKey("voiceFileUpload")
                        .withMessage("文件夹路径不存在，创建文件夹【" + fileFolder + "】").flush();
                boolean isCreateSuccess = file.mkdirs();
                if (isCreateSuccess) {
                    LCLogger.info().withMessageKey("voiceFileUpload")
                            .withMessage("Create folder【" + fileFolder + "】 Success").flush();
                } else {
                    LCLogger.info().withMessageKey("voiceFileUpload")
                            .withMessage("Create folder 【" + fileFolder + "】 Failed").flush();
                }
            }
            if (!file.isDirectory()) {
                // 如果路径不是一个目录， 报错
                LCLogger.error().withMessageKey("voiceFileUpload")
                        .withMessage(fileFolder + "is not a file directory!").flush();
            } else if (!file.canRead()) {
                // 如果路径不可读，报错
                LCLogger.error().withMessageKey("voiceFileUpload")
                        .withMessage("this folder can't be read " + fileFolder).flush();
            } else if (!file.canWrite()) {
                // 如果路径不可写，报错
                LCLogger.error().withMessageKey("voiceFileUpload")
                        .withMessage("this folder can't be write " + fileFolder).flush();
            }
            file = new File(filePath);
            if (file.exists()) {
                throw new ServiceException("PUBLIC-021");
            } else {
                multiFile.transferTo(new File(filePath));
            }
            Long id = keyGenerateService.keyGenerate();
            record.setId(id);
            record.setAttachmentName(fileName);
            File f = new File(YmlConstants.getPortalShareUrlDir() + fileRelatePath);
            if (f.exists() && f.isFile()) {
                record.setAttachmentSize(String.valueOf(f.length()));
            } else {
                record.setAttachmentSize("0");
            }
            record.setModuleType(moduleType);
            record.setAttachmentPath(fileRelatePath);
            record.setAttachmentRealPath(fileRelatePath);
            // 文件类型(后缀)
            String fileType = "";
            try {
                fileType = originalFilename.substring(originalFilename.lastIndexOf('.') + 1).toLowerCase();
            } catch (Exception e) {
                LCLogger.withException(e);
                fileType = "";
            }
            record.setAttachmentOriginal(originalFilename);
            record.setAttachmentType(fileType);
            attachmentMapper.insert(record);
            //获取Attachment数据
            record = attachmentMapper.queryVoiceAttachmentByHeadId(record.getHeadId());

        } catch (Exception e) {
            LCLogger.withException(e);
            return record;
        }
        return record;
    }

    /**
     * 根据Id更新头Id信息
     *
     * @param attachment
     * @throws Exception
     */
    @Override
    public void attachmentHeadIdUpdate(Attachment attachment) throws Exception {
        attachmentMapper.attachmentHeadIdUpdate(attachment);
    }

    /**
     * 批量删除文件
     * @author wangdawei
     * @param attachmentList
     * @return
     */
    @Override
    @Transactional
    public Map<String, Object> batchDeleteAttch(List<Attachment> attachmentList) throws Exception {
        Map<String, Object> result = new HashMap<>(4);
        if (attachmentList == null) {
            throw new ServiceException("PUBLIC-013");
        }
        for (Attachment attachment : attachmentList) {
            this.deleteById(attachment);
        }
        result.put("success",true);
        return result;
    }

    /**
     * 根据filtersRaw查询附件，用来区别之前标准查询方法
     *
     * @author LiYang
     * @date 20191209
     */
    @Override
    public List<Attachment> queryByFilterPage(Attachment attachment) throws Exception {
        beforQueryExample(attachment);
        beforQueryAllOrExample(attachment);
        return attachmentMapper.queryByFilterPage(attachment);
    }

    @Override
    public Attachment insertAttachment(MultipartFile myfile, Attachment entity) throws Exception {
        if (null == entity.getHeadId()) {
            throw new ServiceException("PUBLIC-018");
        }
        if (myfile == null) {
            throw new ServiceException("PUBLIC-013");
        }
        if (StringUtils.isBlank(entity.getModuleType())) {
            throw new ServiceException("PUBLIC-019");
        }
        // 插入附件表
        // 附件上传
        uploadFile(myfile, entity);
        // 向附件表插入附件基础信息
        Long attId = keyGenerateDao.keyGenerate();
        entity.setId(attId);
        entity.setLoginCorpId(102L);
        entity.setCreatedBy(0L);
        entity.setCorpid(102L);
        entity.interfaceCreateData(0L, 0L, 102L);
        attachmentMapper.attachmentInsertByInterface(entity);
        Attachment newEntity = queryById(entity);
        return newEntity;
    }

    /**
     * 无登陆用户情况下上传文件，文件路径设置一个默认的路径
     *
     * @author 路江
     * @date 2019-12-24
     * @param myfile
     * @param record
     * @throws Exception
     */
    private void uploadFile(MultipartFile myfile, Attachment record) throws Exception {
        String realativPath = "/web/file/10000001/" + DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
        String realPath = YmlConstants.getPortalShareFileDir() + realativPath;
        String originalFilename = null;

        originalFilename = myfile.getOriginalFilename();
        if (myfile.isEmpty()) {
            throw new ServiceException("PUBLIC-017");
        } else {
            SimpleDateFormat datetimeFormatter = new SimpleDateFormat("yyyyMMddHHmmsss");
            String newfileName = datetimeFormatter.format(new Date()) + "_" + FileIndexGenarate.getFileIndex(new Date())
                    + originalFilename.substring(originalFilename.lastIndexOf('.'));
            record.setAttachmentName(newfileName);
            record.setAttachmentPath(realativPath + "/" + newfileName);
            record.setAttachmentOriginal(originalFilename);
            record.setAttachmentSize(String.valueOf(myfile.getSize()));
            //文件类型(后缀)
            String fileType = "";
            try {
                fileType = originalFilename.substring(originalFilename.lastIndexOf('.') + 1).toLowerCase();
            } catch (Exception e) {
                LCLogger.withException(e);
                fileType = "";
            }
            record.setAttachmentType(fileType == null ? "" : fileType);
            try {
                FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, newfileName));
            } catch (IOException e) {
                LCLogger.withException(e);
                throw new ServiceException(e.getMessage());
            }
            String jpg = "jpg";
            String png = "png";
            String bmp = "bmp";
            String jpeg = "jpeg";
            if (jpg.equals(fileType) || png.equals(fileType) || bmp.equals(fileType) || jpeg.equals(fileType)) {
                try {
                    String strImg = realPath + "/" + newfileName;
                    strImg = strImg.substring(strImg.lastIndexOf(",") + 1);
                    String binaryImg = getImageBinary(strImg, fileType);
                    UploadImgInfo ui = ImageUtil.saveImageAndLessImage(binaryImg, String.valueOf(10000001), String.valueOf(record.getId()));
                    record.setSmallurl(ui.getFileLessPath());
                    record.setBigurl(ui.getBigfilePath());
                } catch (Exception e) {
                    LCLogger.withException(e);
                }
            }
        }
    }

    @Override
    public Map<String, Object> getTmpSecret(Attachment entity) throws Exception {
        Map<String, Object> result = new HashMap<>(8);
        // 定义临时secret，secretKey，sessionToken，expiredTime对象
        String tmpSecretId;
        String tmpSecretKey;
        String sessionToken;
        String startTime;
        String expiredTime;
        // 获取存储桶bucketId，前端传入
        String bucketId = entity.getBucketId();
        // 获取地区regionId，前端传入
        String regionId = entity.getRegionId();
        LinkRedisClient redis = RedisUtil.getJedis();
        try {
            expiredTime = redis.hget(COS_TMP_SECRET, "expiredTime");
            // 判断临时凭证是否过期，或为空
            boolean tmpExpired = StringUtils.isBlank(expiredTime) || checkTmpExpired(expiredTime);
            if (!tmpExpired) {
                tmpSecretId = redis.hget(COS_TMP_SECRET, "tmpSecretId");
                tmpSecretKey = redis.hget(COS_TMP_SECRET, "tmpSecretKey");
                sessionToken = redis.hget(COS_TMP_SECRET, "sessionToken");
                startTime = redis.hget(COS_TMP_SECRET, "startTime");
            } else {
                TreeMap<String, Object> config = new TreeMap<String, Object>();
                config.put("SecretId", SECRET_ID);
                config.put("SecretKey", SECRET_KEY);
                // 设置临时秘钥有效时长，默认1800秒
                config.put("durationSeconds", 1800);
                // 设置存储桶
                config.put("bucket", bucketId);
                // 设置bucket所在地区
                config.put("region", regionId);
                // 设置允许的路径前缀可以根据自己网站的用户登录态判断允许上传的具体路径，例子：a.jpg 或者 a/* 或者 * 。
                // 如果填写了“*”，将允许用户访问所有资源；除非业务需要，否则请按照最小权限原则授予用户相应的访问权限范围
                config.put("allowPrefix", "*");
                // 秘钥权限列表，包括简单上传，表单上传和分片上传
                String[] allowActions = new String[] {
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传、小程序上传
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload",
                    //下载操作
                    "name/cos:GetObject",
                    // 查询对象列表
                    "name/cos:GetBucket",
                    // 查询对象元数据
                    "name/cos:HeadObject",
                    // 删除单个对象
                    "name/cos:DeleteObject",
                };
                config.put("allowActions", allowActions);
                JSONObject credential = CosStsClient.getCredential(config);
                JSONObject credentials = (JSONObject) credential.get("credentials");
                // 返回临时凭证信息，添加到返回结果Map中，并保存至redis缓存
                tmpSecretId = credentials.get("tmpSecretId").toString();
                tmpSecretKey = credentials.get("tmpSecretKey").toString();
                sessionToken = credentials.get("sessionToken").toString();
                startTime = credential.get("startTime").toString();
                expiredTime = credential.get("expiredTime").toString();
                // 更新redis临时凭证
                redis.hset(COS_TMP_SECRET, "tmpSecretId", tmpSecretId);
                redis.hset(COS_TMP_SECRET, "tmpSecretKey", tmpSecretKey);
                redis.hset(COS_TMP_SECRET, "sessionToken", sessionToken);
                redis.hset(COS_TMP_SECRET, "startTime", startTime);
                redis.hset(COS_TMP_SECRET, "expiredTime", expiredTime);
            }
            result.put("tmpSecretId", tmpSecretId);
            result.put("tmpSecretKey", tmpSecretKey);
            result.put("sessionToken", sessionToken);
            // 过期时间，unix时间戳，考虑做成缓存，每次请求校验过期时间
            result.put("startTime", startTime);
            result.put("expiredTime", expiredTime);
        } catch (Exception e) {
            throw new IllegalArgumentException("no valid secret !");
        }
        return result;
    }

    /**
     * 根据附件id获取单个附件临时url
     *
     * @author 李莹
     * @date 2020/07/03 11:36
     */
    @Override
    public Map<String, Object> getSingleAttachmentUrl(Attachment attachment) throws Exception {
        Map<String, Object> result = new HashMap<>(8);
        // 临时secretId
        String tmpSecretId = attachment.getTmpSecretId();
        if (StringUtils.isBlank(tmpSecretId)) {
            throw new BasicServiceException("请传入tmpSecretId");
        }
        // 临时secretKey
        String tmpSecretKey = attachment.getTmpSecretKey();
        if (StringUtils.isBlank(tmpSecretKey)) {
            throw new BasicServiceException("请传入tmpSecretKey");
        }
        // 临时sessionToken
        String sessionToken = attachment.getSessionToken();
        if (StringUtils.isBlank(sessionToken)) {
            throw new BasicServiceException("请传入sessionToken");
        }
        if (null == attachment.getId()) {
            throw new BasicServiceException("请传入附件id");
        }
        // 获取当前附件信息
        attachment = attachmentMapper.queryById(attachment);
        // 获取key值
        String attachmentPath = attachment.getAttachmentPath();
        // mapper获取的attachmentPath是拼接了路径前缀后的结果，如https://crm.linkcrm.cn/share/1.png，需截取1.png作为key值
        String key = getKeyFromPath(attachmentPath);
        // 存储桶id
        String bucketId = attachment.getBucketId();
        // 地域id
        String regionId = attachment.getRegionId();
        // 传入临时秘钥，获取权限
        BasicSessionCredentials credentials = new BasicSessionCredentials(tmpSecretId, tmpSecretKey, sessionToken);
        // 设置bucket地域
        ClientConfig config = new ClientConfig(new Region(regionId));
        // 生成cos客户端
        COSClient client = new COSClient(credentials, config);
        // 创建预签名请求
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketId, key, HttpMethodName.GET);
        // 设置下载时返回的http头
        ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
        String responseContentType = "image/x-icon";
        String responseContentLanguage = "zh-CN";
        String responseContentDispositon = "filename=\"" + key + "\"";
        String responseCacheControl = "no-cache";
        String cacheExpireStr =
                DateUtils.formatRFC822Date(new Date(System.currentTimeMillis() + 24L * 3600L * 1000L));
        responseHeaders.setContentType(responseContentType);
        responseHeaders.setContentLanguage(responseContentLanguage);
        responseHeaders.setContentDisposition(responseContentDispositon);
        responseHeaders.setCacheControl(responseCacheControl);
        responseHeaders.setExpires(cacheExpireStr);
        req.setResponseHeaders(responseHeaders);
        // 设置签名过期时间(可选)，若未进行设置，则默认使用 ClientConfig 中的签名过期时间(1小时)
        // 这里设置签名在半个小时后过期
        Date expirationDate = new Date(System.currentTimeMillis() + 30L * 60L * 1000L);
        req.setExpiration(expirationDate);
        String url = client.generatePresignedUrl(req).toString();
        result.put("result", url);
        // 关闭客户端
        client.shutdown();
        return result;
    }

    /**
     * 判断临时凭证是否过期，传入redis缓存中的过期时间
     *
     * @author 李莹
     * @date 2020/07/06 10:41
     */
    private static Boolean checkTmpExpired(String time) {
        long expiredTime = Long.parseLong(time) * 1000;
        long now = System.currentTimeMillis();
        return now > expiredTime;
    }

    /**
     * sql查询到的数据对smallUrl，attachmentPath等字段做了域名拼接处理
     * cos使用这两个字段时不许域名部分，需做截取
     * @author 李莹
     * @date 2020/07/06 08:30
     */
    private static String getKeyFromPath(String oriPath) {
        String pathPrefix = "https://crm.linkcrm.cn/share/";
        return oriPath.substring(pathPrefix.length());
    }

    @Override
    public Attachment queryById(Attachment entity) throws Exception {
        String cosUploadType = "cos";
        Attachment attachment = attachmentMapper.queryById(entity);
        String uploadType = attachment.getUploadType();
        if (StringUtils.isNotBlank(uploadType) && cosUploadType.equals(uploadType)) {
            attachment = interceptUrl(attachment);
        }
        return attachment;
    }

    @Override
    public List<Attachment> queryByExamplePage(Attachment entity) throws Exception {
        List<Attachment> list;
        // 腾讯云COS上传类型为"cos"，原附件上传类型为"origin"
        String originUploadType = "origin";
        String cosUploadType = "cos";
        String uploadType = entity.getUploadType();
        String checkProperty = "uploadType";
        String checkValue = "cos";
        String checkOpt = "=";
        // 是否筛选COS数据判断标识，条件uploadType不为空且值为"cos" 或者 filtersRaw中作为筛选条件，uploadType == "cos"
        boolean flag = StringUtils.isNotBlank(uploadType) && cosUploadType.equals(uploadType)
                || checkQpsFilter(entity, checkProperty, checkValue, checkOpt);
        if (flag) {
            // 满足查询cos条件时，查询cos上传数据
            // mapper获取的attachmentPath是拼接了路径前缀后的结果，如https://crm.linkcrm.cn/share/1.png，需截取1.png作为key值
            // 若要获取COS上传的图片时，对smallUrl，attachmentPath做截取
            list = attachmentMapper.queryByExamplePage(entity);
            for (Attachment item: list) {
                item = interceptUrl(item);
            }
        } else {
            // 若未传递uploadType数据，默认查询原始上传数据
            entity.setUploadType(originUploadType);
            list = attachmentMapper.queryByExamplePage(entity);
        }
        return list;
    }

    /**
     * 筛选cos数据时，需对attachmentPath和smallurl字段做截取处理
     *
     * @author 李莹
     * @date 2020/07/10 11:31
     */
    private Attachment interceptUrl(Attachment attachment) throws Exception {
        String path = attachment.getAttachmentPath();
        String smallPath = attachment.getSmallurl();
        if (StringUtils.isNotBlank(path)) {
            String key = getKeyFromPath(attachment.getAttachmentPath());
            attachment.setAttachmentPath(key);
        }
        if (StringUtils.isNotBlank(smallPath)) {
            String smallKey = getKeyFromPath(attachment.getSmallurl());
            attachment.setSmallurl(smallKey);
        }
        return attachment;
    }

    /**
     * 判断某个属性是否在qps中作为筛选参数
     *
     * @author 李莹
     * @date 2020/07/07 09:14
     */
    private static Boolean checkQpsFilter(Attachment attachment, String property, String value, String operator) throws Exception {
        QueryParams qps = attachment.getQps();
        boolean result = false;
        if (qps != null) {
            List<Filter> filters = qps.getFilters();
            if (filters.size() > 0) {
                for (Filter filter: filters) {
                    result = property.equals(filter.getProperty()) && value.equals(filter.getValue()) && operator.equals(filter.getOperator());
                    if (result) {
                        break;
                    }
                }
            }
        }

        return result;
    }
}
